#![allow(unreachable_patterns)]

use libc::c_char;
use std::ffi::{CStr, CString};
use std::mem::transmute;

use crate::sys;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(i32)]
pub enum Keycode {
    Backspace = sys::SDLK_BACKSPACE as i32,
    Tab = sys::SDLK_TAB as i32,
    Return = sys::SDLK_RETURN as i32,
    Escape = sys::SDLK_ESCAPE as i32,
    Space = sys::SDLK_SPACE as i32,
    Exclaim = sys::SDLK_EXCLAIM as i32,
    Quotedbl = sys::SDLK_QUOTEDBL as i32,
    Hash = sys::SDLK_HASH as i32,
    Dollar = sys::SDLK_DOLLAR as i32,
    Percent = sys::SDLK_PERCENT as i32,
    Ampersand = sys::SDLK_AMPERSAND as i32,
    Quote = sys::SDLK_QUOTE as i32,
    LeftParen = sys::SDLK_LEFTPAREN as i32,
    RightParen = sys::SDLK_RIGHTPAREN as i32,
    Asterisk = sys::SDLK_ASTERISK as i32,
    Plus = sys::SDLK_PLUS as i32,
    Comma = sys::SDLK_COMMA as i32,
    Minus = sys::SDLK_MINUS as i32,
    Period = sys::SDLK_PERIOD as i32,
    Slash = sys::SDLK_SLASH as i32,
    Num0 = sys::SDLK_0 as i32,
    Num1 = sys::SDLK_1 as i32,
    Num2 = sys::SDLK_2 as i32,
    Num3 = sys::SDLK_3 as i32,
    Num4 = sys::SDLK_4 as i32,
    Num5 = sys::SDLK_5 as i32,
    Num6 = sys::SDLK_6 as i32,
    Num7 = sys::SDLK_7 as i32,
    Num8 = sys::SDLK_8 as i32,
    Num9 = sys::SDLK_9 as i32,
    Colon = sys::SDLK_COLON as i32,
    Semicolon = sys::SDLK_SEMICOLON as i32,
    Less = sys::SDLK_LESS as i32,
    Equals = sys::SDLK_EQUALS as i32,
    Greater = sys::SDLK_GREATER as i32,
    Question = sys::SDLK_QUESTION as i32,
    At = sys::SDLK_AT as i32,
    LeftBracket = sys::SDLK_LEFTBRACKET as i32,
    Backslash = sys::SDLK_BACKSLASH as i32,
    RightBracket = sys::SDLK_RIGHTBRACKET as i32,
    Caret = sys::SDLK_CARET as i32,
    Underscore = sys::SDLK_UNDERSCORE as i32,
    Backquote = sys::SDLK_BACKQUOTE as i32,
    A = sys::SDLK_a as i32,
    B = sys::SDLK_b as i32,
    C = sys::SDLK_c as i32,
    D = sys::SDLK_d as i32,
    E = sys::SDLK_e as i32,
    F = sys::SDLK_f as i32,
    G = sys::SDLK_g as i32,
    H = sys::SDLK_h as i32,
    I = sys::SDLK_i as i32,
    J = sys::SDLK_j as i32,
    K = sys::SDLK_k as i32,
    L = sys::SDLK_l as i32,
    M = sys::SDLK_m as i32,
    N = sys::SDLK_n as i32,
    O = sys::SDLK_o as i32,
    P = sys::SDLK_p as i32,
    Q = sys::SDLK_q as i32,
    R = sys::SDLK_r as i32,
    S = sys::SDLK_s as i32,
    T = sys::SDLK_t as i32,
    U = sys::SDLK_u as i32,
    V = sys::SDLK_v as i32,
    W = sys::SDLK_w as i32,
    X = sys::SDLK_x as i32,
    Y = sys::SDLK_y as i32,
    Z = sys::SDLK_z as i32,
    Delete = sys::SDLK_DELETE as i32,
    CapsLock = sys::SDLK_CAPSLOCK as i32,
    F1 = sys::SDLK_F1 as i32,
    F2 = sys::SDLK_F2 as i32,
    F3 = sys::SDLK_F3 as i32,
    F4 = sys::SDLK_F4 as i32,
    F5 = sys::SDLK_F5 as i32,
    F6 = sys::SDLK_F6 as i32,
    F7 = sys::SDLK_F7 as i32,
    F8 = sys::SDLK_F8 as i32,
    F9 = sys::SDLK_F9 as i32,
    F10 = sys::SDLK_F10 as i32,
    F11 = sys::SDLK_F11 as i32,
    F12 = sys::SDLK_F12 as i32,
    PrintScreen = sys::SDLK_PRINTSCREEN as i32,
    ScrollLock = sys::SDLK_SCROLLLOCK as i32,
    Pause = sys::SDLK_PAUSE as i32,
    Insert = sys::SDLK_INSERT as i32,
    Home = sys::SDLK_HOME as i32,
    PageUp = sys::SDLK_PAGEUP as i32,
    End = sys::SDLK_END as i32,
    PageDown = sys::SDLK_PAGEDOWN as i32,
    Right = sys::SDLK_RIGHT as i32,
    Left = sys::SDLK_LEFT as i32,
    Down = sys::SDLK_DOWN as i32,
    Up = sys::SDLK_UP as i32,
    NumLockClear = sys::SDLK_NUMLOCKCLEAR as i32,
    KpDivide = sys::SDLK_KP_DIVIDE as i32,
    KpMultiply = sys::SDLK_KP_MULTIPLY as i32,
    KpMinus = sys::SDLK_KP_MINUS as i32,
    KpPlus = sys::SDLK_KP_PLUS as i32,
    KpEnter = sys::SDLK_KP_ENTER as i32,
    Kp1 = sys::SDLK_KP_1 as i32,
    Kp2 = sys::SDLK_KP_2 as i32,
    Kp3 = sys::SDLK_KP_3 as i32,
    Kp4 = sys::SDLK_KP_4 as i32,
    Kp5 = sys::SDLK_KP_5 as i32,
    Kp6 = sys::SDLK_KP_6 as i32,
    Kp7 = sys::SDLK_KP_7 as i32,
    Kp8 = sys::SDLK_KP_8 as i32,
    Kp9 = sys::SDLK_KP_9 as i32,
    Kp0 = sys::SDLK_KP_0 as i32,
    KpPeriod = sys::SDLK_KP_PERIOD as i32,
    Application = sys::SDLK_APPLICATION as i32,
    Power = sys::SDLK_POWER as i32,
    KpEquals = sys::SDLK_KP_EQUALS as i32,
    F13 = sys::SDLK_F13 as i32,
    F14 = sys::SDLK_F14 as i32,
    F15 = sys::SDLK_F15 as i32,
    F16 = sys::SDLK_F16 as i32,
    F17 = sys::SDLK_F17 as i32,
    F18 = sys::SDLK_F18 as i32,
    F19 = sys::SDLK_F19 as i32,
    F20 = sys::SDLK_F20 as i32,
    F21 = sys::SDLK_F21 as i32,
    F22 = sys::SDLK_F22 as i32,
    F23 = sys::SDLK_F23 as i32,
    F24 = sys::SDLK_F24 as i32,
    Execute = sys::SDLK_EXECUTE as i32,
    Help = sys::SDLK_HELP as i32,
    Menu = sys::SDLK_MENU as i32,
    Select = sys::SDLK_SELECT as i32,
    Stop = sys::SDLK_STOP as i32,
    Again = sys::SDLK_AGAIN as i32,
    Undo = sys::SDLK_UNDO as i32,
    Cut = sys::SDLK_CUT as i32,
    Copy = sys::SDLK_COPY as i32,
    Paste = sys::SDLK_PASTE as i32,
    Find = sys::SDLK_FIND as i32,
    Mute = sys::SDLK_MUTE as i32,
    VolumeUp = sys::SDLK_VOLUMEUP as i32,
    VolumeDown = sys::SDLK_VOLUMEDOWN as i32,
    KpComma = sys::SDLK_KP_COMMA as i32,
    KpEqualsAS400 = sys::SDLK_KP_EQUALSAS400 as i32,
    AltErase = sys::SDLK_ALTERASE as i32,
    Sysreq = sys::SDLK_SYSREQ as i32,
    Cancel = sys::SDLK_CANCEL as i32,
    Clear = sys::SDLK_CLEAR as i32,
    Prior = sys::SDLK_PRIOR as i32,
    Return2 = sys::SDLK_RETURN2 as i32,
    Separator = sys::SDLK_SEPARATOR as i32,
    Out = sys::SDLK_OUT as i32,
    Oper = sys::SDLK_OPER as i32,
    ClearAgain = sys::SDLK_CLEARAGAIN as i32,
    CrSel = sys::SDLK_CRSEL as i32,
    ExSel = sys::SDLK_EXSEL as i32,
    Kp00 = sys::SDLK_KP_00 as i32,
    Kp000 = sys::SDLK_KP_000 as i32,
    ThousandsSeparator = sys::SDLK_THOUSANDSSEPARATOR as i32,
    DecimalSeparator = sys::SDLK_DECIMALSEPARATOR as i32,
    CurrencyUnit = sys::SDLK_CURRENCYUNIT as i32,
    CurrencySubUnit = sys::SDLK_CURRENCYSUBUNIT as i32,
    KpLeftParen = sys::SDLK_KP_LEFTPAREN as i32,
    KpRightParen = sys::SDLK_KP_RIGHTPAREN as i32,
    KpLeftBrace = sys::SDLK_KP_LEFTBRACE as i32,
    KpRightBrace = sys::SDLK_KP_RIGHTBRACE as i32,
    KpTab = sys::SDLK_KP_TAB as i32,
    KpBackspace = sys::SDLK_KP_BACKSPACE as i32,
    KpA = sys::SDLK_KP_A as i32,
    KpB = sys::SDLK_KP_B as i32,
    KpC = sys::SDLK_KP_C as i32,
    KpD = sys::SDLK_KP_D as i32,
    KpE = sys::SDLK_KP_E as i32,
    KpF = sys::SDLK_KP_F as i32,
    KpXor = sys::SDLK_KP_XOR as i32,
    KpPower = sys::SDLK_KP_POWER as i32,
    KpPercent = sys::SDLK_KP_PERCENT as i32,
    KpLess = sys::SDLK_KP_LESS as i32,
    KpGreater = sys::SDLK_KP_GREATER as i32,
    KpAmpersand = sys::SDLK_KP_AMPERSAND as i32,
    KpDblAmpersand = sys::SDLK_KP_DBLAMPERSAND as i32,
    KpVerticalBar = sys::SDLK_KP_VERTICALBAR as i32,
    KpDblVerticalBar = sys::SDLK_KP_DBLVERTICALBAR as i32,
    KpColon = sys::SDLK_KP_COLON as i32,
    KpHash = sys::SDLK_KP_HASH as i32,
    KpSpace = sys::SDLK_KP_SPACE as i32,
    KpAt = sys::SDLK_KP_AT as i32,
    KpExclam = sys::SDLK_KP_EXCLAM as i32,
    KpMemStore = sys::SDLK_KP_MEMSTORE as i32,
    KpMemRecall = sys::SDLK_KP_MEMRECALL as i32,
    KpMemClear = sys::SDLK_KP_MEMCLEAR as i32,
    KpMemAdd = sys::SDLK_KP_MEMADD as i32,
    KpMemSubtract = sys::SDLK_KP_MEMSUBTRACT as i32,
    KpMemMultiply = sys::SDLK_KP_MEMMULTIPLY as i32,
    KpMemDivide = sys::SDLK_KP_MEMDIVIDE as i32,
    KpPlusMinus = sys::SDLK_KP_PLUSMINUS as i32,
    KpClear = sys::SDLK_KP_CLEAR as i32,
    KpClearEntry = sys::SDLK_KP_CLEARENTRY as i32,
    KpBinary = sys::SDLK_KP_BINARY as i32,
    KpOctal = sys::SDLK_KP_OCTAL as i32,
    KpDecimal = sys::SDLK_KP_DECIMAL as i32,
    KpHexadecimal = sys::SDLK_KP_HEXADECIMAL as i32,
    LCtrl = sys::SDLK_LCTRL as i32,
    LShift = sys::SDLK_LSHIFT as i32,
    LAlt = sys::SDLK_LALT as i32,
    LGui = sys::SDLK_LGUI as i32,
    RCtrl = sys::SDLK_RCTRL as i32,
    RShift = sys::SDLK_RSHIFT as i32,
    RAlt = sys::SDLK_RALT as i32,
    RGui = sys::SDLK_RGUI as i32,
    Mode = sys::SDLK_MODE as i32,
    AudioNext = sys::SDLK_AUDIONEXT as i32,
    AudioPrev = sys::SDLK_AUDIOPREV as i32,
    AudioStop = sys::SDLK_AUDIOSTOP as i32,
    AudioPlay = sys::SDLK_AUDIOPLAY as i32,
    AudioMute = sys::SDLK_AUDIOMUTE as i32,
    MediaSelect = sys::SDLK_MEDIASELECT as i32,
    Www = sys::SDLK_WWW as i32,
    Mail = sys::SDLK_MAIL as i32,
    Calculator = sys::SDLK_CALCULATOR as i32,
    Computer = sys::SDLK_COMPUTER as i32,
    AcSearch = sys::SDLK_AC_SEARCH as i32,
    AcHome = sys::SDLK_AC_HOME as i32,
    AcBack = sys::SDLK_AC_BACK as i32,
    AcForward = sys::SDLK_AC_FORWARD as i32,
    AcStop = sys::SDLK_AC_STOP as i32,
    AcRefresh = sys::SDLK_AC_REFRESH as i32,
    AcBookmarks = sys::SDLK_AC_BOOKMARKS as i32,
    BrightnessDown = sys::SDLK_BRIGHTNESSDOWN as i32,
    BrightnessUp = sys::SDLK_BRIGHTNESSUP as i32,
    DisplaySwitch = sys::SDLK_DISPLAYSWITCH as i32,
    KbdIllumToggle = sys::SDLK_KBDILLUMTOGGLE as i32,
    KbdIllumDown = sys::SDLK_KBDILLUMDOWN as i32,
    KbdIllumUp = sys::SDLK_KBDILLUMUP as i32,
    Eject = sys::SDLK_EJECT as i32,
    Sleep = sys::SDLK_SLEEP as i32,
}

impl Keycode {
    pub fn from_i32(n: i32) -> Option<Keycode> {
        use self::Keycode::*;
        let n = n as u32;

        Some(match unsafe { transmute(n) } {
            sys::SDLK_BACKSPACE => Backspace,
            sys::SDLK_TAB => Tab,
            sys::SDLK_RETURN => Return,
            sys::SDLK_ESCAPE => Escape,
            sys::SDLK_SPACE => Space,
            sys::SDLK_EXCLAIM => Exclaim,
            sys::SDLK_QUOTEDBL => Quotedbl,
            sys::SDLK_HASH => Hash,
            sys::SDLK_DOLLAR => Dollar,
            sys::SDLK_PERCENT => Percent,
            sys::SDLK_AMPERSAND => Ampersand,
            sys::SDLK_QUOTE => Quote,
            sys::SDLK_LEFTPAREN => LeftParen,
            sys::SDLK_RIGHTPAREN => RightParen,
            sys::SDLK_ASTERISK => Asterisk,
            sys::SDLK_PLUS => Plus,
            sys::SDLK_COMMA => Comma,
            sys::SDLK_MINUS => Minus,
            sys::SDLK_PERIOD => Period,
            sys::SDLK_SLASH => Slash,
            sys::SDLK_0 => Num0,
            sys::SDLK_1 => Num1,
            sys::SDLK_2 => Num2,
            sys::SDLK_3 => Num3,
            sys::SDLK_4 => Num4,
            sys::SDLK_5 => Num5,
            sys::SDLK_6 => Num6,
            sys::SDLK_7 => Num7,
            sys::SDLK_8 => Num8,
            sys::SDLK_9 => Num9,
            sys::SDLK_COLON => Colon,
            sys::SDLK_SEMICOLON => Semicolon,
            sys::SDLK_LESS => Less,
            sys::SDLK_EQUALS => Equals,
            sys::SDLK_GREATER => Greater,
            sys::SDLK_QUESTION => Question,
            sys::SDLK_AT => At,
            sys::SDLK_LEFTBRACKET => LeftBracket,
            sys::SDLK_BACKSLASH => Backslash,
            sys::SDLK_RIGHTBRACKET => RightBracket,
            sys::SDLK_CARET => Caret,
            sys::SDLK_UNDERSCORE => Underscore,
            sys::SDLK_BACKQUOTE => Backquote,
            sys::SDLK_a => A,
            sys::SDLK_b => B,
            sys::SDLK_c => C,
            sys::SDLK_d => D,
            sys::SDLK_e => E,
            sys::SDLK_f => F,
            sys::SDLK_g => G,
            sys::SDLK_h => H,
            sys::SDLK_i => I,
            sys::SDLK_j => J,
            sys::SDLK_k => K,
            sys::SDLK_l => L,
            sys::SDLK_m => M,
            sys::SDLK_n => N,
            sys::SDLK_o => O,
            sys::SDLK_p => P,
            sys::SDLK_q => Q,
            sys::SDLK_r => R,
            sys::SDLK_s => S,
            sys::SDLK_t => T,
            sys::SDLK_u => U,
            sys::SDLK_v => V,
            sys::SDLK_w => W,
            sys::SDLK_x => X,
            sys::SDLK_y => Y,
            sys::SDLK_z => Z,
            sys::SDLK_DELETE => Delete,
            sys::SDLK_CAPSLOCK => CapsLock,
            sys::SDLK_F1 => F1,
            sys::SDLK_F2 => F2,
            sys::SDLK_F3 => F3,
            sys::SDLK_F4 => F4,
            sys::SDLK_F5 => F5,
            sys::SDLK_F6 => F6,
            sys::SDLK_F7 => F7,
            sys::SDLK_F8 => F8,
            sys::SDLK_F9 => F9,
            sys::SDLK_F10 => F10,
            sys::SDLK_F11 => F11,
            sys::SDLK_F12 => F12,
            sys::SDLK_PRINTSCREEN => PrintScreen,
            sys::SDLK_SCROLLLOCK => ScrollLock,
            sys::SDLK_PAUSE => Pause,
            sys::SDLK_INSERT => Insert,
            sys::SDLK_HOME => Home,
            sys::SDLK_PAGEUP => PageUp,
            sys::SDLK_END => End,
            sys::SDLK_PAGEDOWN => PageDown,
            sys::SDLK_RIGHT => Right,
            sys::SDLK_LEFT => Left,
            sys::SDLK_DOWN => Down,
            sys::SDLK_UP => Up,
            sys::SDLK_NUMLOCKCLEAR => NumLockClear,
            sys::SDLK_KP_DIVIDE => KpDivide,
            sys::SDLK_KP_MULTIPLY => KpMultiply,
            sys::SDLK_KP_MINUS => KpMinus,
            sys::SDLK_KP_PLUS => KpPlus,
            sys::SDLK_KP_ENTER => KpEnter,
            sys::SDLK_KP_1 => Kp1,
            sys::SDLK_KP_2 => Kp2,
            sys::SDLK_KP_3 => Kp3,
            sys::SDLK_KP_4 => Kp4,
            sys::SDLK_KP_5 => Kp5,
            sys::SDLK_KP_6 => Kp6,
            sys::SDLK_KP_7 => Kp7,
            sys::SDLK_KP_8 => Kp8,
            sys::SDLK_KP_9 => Kp9,
            sys::SDLK_KP_0 => Kp0,
            sys::SDLK_KP_PERIOD => KpPeriod,
            sys::SDLK_APPLICATION => Application,
            sys::SDLK_POWER => Power,
            sys::SDLK_KP_EQUALS => KpEquals,
            sys::SDLK_F13 => F13,
            sys::SDLK_F14 => F14,
            sys::SDLK_F15 => F15,
            sys::SDLK_F16 => F16,
            sys::SDLK_F17 => F17,
            sys::SDLK_F18 => F18,
            sys::SDLK_F19 => F19,
            sys::SDLK_F20 => F20,
            sys::SDLK_F21 => F21,
            sys::SDLK_F22 => F22,
            sys::SDLK_F23 => F23,
            sys::SDLK_F24 => F24,
            sys::SDLK_EXECUTE => Execute,
            sys::SDLK_HELP => Help,
            sys::SDLK_MENU => Menu,
            sys::SDLK_SELECT => Select,
            sys::SDLK_STOP => Stop,
            sys::SDLK_AGAIN => Again,
            sys::SDLK_UNDO => Undo,
            sys::SDLK_CUT => Cut,
            sys::SDLK_COPY => Copy,
            sys::SDLK_PASTE => Paste,
            sys::SDLK_FIND => Find,
            sys::SDLK_MUTE => Mute,
            sys::SDLK_VOLUMEUP => VolumeUp,
            sys::SDLK_VOLUMEDOWN => VolumeDown,
            sys::SDLK_KP_COMMA => KpComma,
            sys::SDLK_KP_EQUALSAS400 => KpEqualsAS400,
            sys::SDLK_ALTERASE => AltErase,
            sys::SDLK_SYSREQ => Sysreq,
            sys::SDLK_CANCEL => Cancel,
            sys::SDLK_CLEAR => Clear,
            sys::SDLK_PRIOR => Prior,
            sys::SDLK_RETURN2 => Return2,
            sys::SDLK_SEPARATOR => Separator,
            sys::SDLK_OUT => Out,
            sys::SDLK_OPER => Oper,
            sys::SDLK_CLEARAGAIN => ClearAgain,
            sys::SDLK_CRSEL => CrSel,
            sys::SDLK_EXSEL => ExSel,
            sys::SDLK_KP_00 => Kp00,
            sys::SDLK_KP_000 => Kp000,
            sys::SDLK_THOUSANDSSEPARATOR => ThousandsSeparator,
            sys::SDLK_DECIMALSEPARATOR => DecimalSeparator,
            sys::SDLK_CURRENCYUNIT => CurrencyUnit,
            sys::SDLK_CURRENCYSUBUNIT => CurrencySubUnit,
            sys::SDLK_KP_LEFTPAREN => KpLeftParen,
            sys::SDLK_KP_RIGHTPAREN => KpRightParen,
            sys::SDLK_KP_LEFTBRACE => KpLeftBrace,
            sys::SDLK_KP_RIGHTBRACE => KpRightBrace,
            sys::SDLK_KP_TAB => KpTab,
            sys::SDLK_KP_BACKSPACE => KpBackspace,
            sys::SDLK_KP_A => KpA,
            sys::SDLK_KP_B => KpB,
            sys::SDLK_KP_C => KpC,
            sys::SDLK_KP_D => KpD,
            sys::SDLK_KP_E => KpE,
            sys::SDLK_KP_F => KpF,
            sys::SDLK_KP_XOR => KpXor,
            sys::SDLK_KP_POWER => KpPower,
            sys::SDLK_KP_PERCENT => KpPercent,
            sys::SDLK_KP_LESS => KpLess,
            sys::SDLK_KP_GREATER => KpGreater,
            sys::SDLK_KP_AMPERSAND => KpAmpersand,
            sys::SDLK_KP_DBLAMPERSAND => KpDblAmpersand,
            sys::SDLK_KP_VERTICALBAR => KpVerticalBar,
            sys::SDLK_KP_DBLVERTICALBAR => KpDblVerticalBar,
            sys::SDLK_KP_COLON => KpColon,
            sys::SDLK_KP_HASH => KpHash,
            sys::SDLK_KP_SPACE => KpSpace,
            sys::SDLK_KP_AT => KpAt,
            sys::SDLK_KP_EXCLAM => KpExclam,
            sys::SDLK_KP_MEMSTORE => KpMemStore,
            sys::SDLK_KP_MEMRECALL => KpMemRecall,
            sys::SDLK_KP_MEMCLEAR => KpMemClear,
            sys::SDLK_KP_MEMADD => KpMemAdd,
            sys::SDLK_KP_MEMSUBTRACT => KpMemSubtract,
            sys::SDLK_KP_MEMMULTIPLY => KpMemMultiply,
            sys::SDLK_KP_MEMDIVIDE => KpMemDivide,
            sys::SDLK_KP_PLUSMINUS => KpPlusMinus,
            sys::SDLK_KP_CLEAR => KpClear,
            sys::SDLK_KP_CLEARENTRY => KpClearEntry,
            sys::SDLK_KP_BINARY => KpBinary,
            sys::SDLK_KP_OCTAL => KpOctal,
            sys::SDLK_KP_DECIMAL => KpDecimal,
            sys::SDLK_KP_HEXADECIMAL => KpHexadecimal,
            sys::SDLK_LCTRL => LCtrl,
            sys::SDLK_LSHIFT => LShift,
            sys::SDLK_LALT => LAlt,
            sys::SDLK_LGUI => LGui,
            sys::SDLK_RCTRL => RCtrl,
            sys::SDLK_RSHIFT => RShift,
            sys::SDLK_RALT => RAlt,
            sys::SDLK_RGUI => RGui,
            sys::SDLK_MODE => Mode,
            sys::SDLK_AUDIONEXT => AudioNext,
            sys::SDLK_AUDIOPREV => AudioPrev,
            sys::SDLK_AUDIOSTOP => AudioStop,
            sys::SDLK_AUDIOPLAY => AudioPlay,
            sys::SDLK_AUDIOMUTE => AudioMute,
            sys::SDLK_MEDIASELECT => MediaSelect,
            sys::SDLK_WWW => Www,
            sys::SDLK_MAIL => Mail,
            sys::SDLK_CALCULATOR => Calculator,
            sys::SDLK_COMPUTER => Computer,
            sys::SDLK_AC_SEARCH => AcSearch,
            sys::SDLK_AC_HOME => AcHome,
            sys::SDLK_AC_BACK => AcBack,
            sys::SDLK_AC_FORWARD => AcForward,
            sys::SDLK_AC_STOP => AcStop,
            sys::SDLK_AC_REFRESH => AcRefresh,
            sys::SDLK_AC_BOOKMARKS => AcBookmarks,
            sys::SDLK_BRIGHTNESSDOWN => BrightnessDown,
            sys::SDLK_BRIGHTNESSUP => BrightnessUp,
            sys::SDLK_DISPLAYSWITCH => DisplaySwitch,
            sys::SDLK_KBDILLUMTOGGLE => KbdIllumToggle,
            sys::SDLK_KBDILLUMDOWN => KbdIllumDown,
            sys::SDLK_KBDILLUMUP => KbdIllumUp,
            sys::SDLK_EJECT => Eject,
            sys::SDLK_SLEEP => Sleep,
            _ => return None,
        })
    }
}

use std::fmt;

impl fmt::Display for Keycode {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.name())
    }
}

use crate::keyboard::Scancode;

impl Keycode {
    /// Gets the virtual key from a scancode. Returns None if there is no corresponding virtual key.
    #[doc(alias = "SDL_GetKeyFromScancode")]
    pub fn from_scancode(scancode: Scancode) -> Option<Keycode> {
        const UNKNOWN: i32 = sys::SDLK_UNKNOWN as i32;
        unsafe {
            match sys::SDL_GetKeyFromScancode(transmute::<u32, sys::SDL_Scancode>(scancode as u32))
            {
                UNKNOWN => None,
                keycode_id => Keycode::from_i32(keycode_id as i32),
            }
        }
    }

    #[doc(alias = "SDL_GetKeyFromName")]
    pub fn from_name(name: &str) -> Option<Keycode> {
        const UNKNOWN: i32 = sys::SDLK_UNKNOWN as i32;
        unsafe {
            match CString::new(name) {
                Ok(name) => match sys::SDL_GetKeyFromName(name.as_ptr() as *const c_char) {
                    UNKNOWN => None,
                    keycode_id => Some(Keycode::from_i32(keycode_id as i32).unwrap()),
                },
                // string contains a nul byte - it won't match anything.
                Err(_) => None,
            }
        }
    }

    #[doc(alias = "SDL_GetKeyName")]
    pub fn name(self) -> String {
        // The name string pointer's contents _might_ change, depending on the last call to SDL_GetKeyName.
        // Knowing this, we must always return a new string.
        unsafe {
            let buf = sys::SDL_GetKeyName(self as i32);
            CStr::from_ptr(buf as *const _).to_str().unwrap().to_owned()
        }
    }
}
